#ifndef SPW_UNI_PTR_H
#define SPW_UNI_PTR_H

#include <type_traits>
#include <utility/spw_pair_in.h>
#include <spw_config.h>

#if USE_DEBUG
#include <cassert>
#endif

namespace spwstd {
namespace spwptr {
/*   unique_ptr<T, deleter>(p,d)
* 1. unique_ptr 的deleter类型必须是, 函数对象类型[函数指针/引用，函数对象，std::function<函数类型>]或其左值引用. 不接受右值
* 2. unique_ptr<T, deleter>(p,d)内Pointer. deleter内有Pointer，为该Pointer; 无则为T*
* 3. deleter为A&或const A&时, d为右值时，构造函数=deleter. 
* 4. 可以利用deleter::pointer, 让T作为Tag使用. 
*/
// DefaultDelete 空类
template<typename Tp>
struct DefaultDelete
{
    constexpr DefaultDelete() noexcept = default;

    template<typename Up, typename = typename std::enable_if_t<std::is_convertible_v<Up*, Tp*>>>
    DefaultDelete(const DefaultDelete<Up>&) noexcept { }

    void operator()(Tp* ptr) const
    {
        // Tp 必须不为void
        static_assert(!std::is_void_v<Tp>, "cant delete pointer to incomplete type");
        // Tp 必须为空类，sizeof(Tp) == 0
        static_assert(sizeof(Tp) > 0, "cant delete pointer to incomplete type");
        delete ptr;
    }
};

template<typename Tp>
struct DefaultDelete<Tp[]>
{
    constexpr DefaultDelete() noexcept = default;

    template<typename Up, typename = typename std::enable_if_t<std::is_convertible_v<Up(*)[], Tp(*)[]>>>
    DefaultDelete(const DefaultDelete<Up[]>&) noexcept { }

    template<typename Up>
    typename std::enable_if_t<std::is_convertible_v<Up(*)[], Tp(*)[]>>
    operator()(Up* ptr) const
    {
        // Tp 必须为空类，sizeof(Tp) == 0
        static_assert(sizeof(Tp) > 0, "cant delete pointer to incomplete type");
        delete [] ptr;
    }
};

template<typename Tp, typename Dp>
class UniquePtrImpl_in 
{
    template<typename Up, typename Ep, typename = void>
    struct Ptr_in 
    {
        using Type = Up*;
    };

    template<typename Up, typename Ep>
    struct Ptr_in<Up, Ep, std::__void_t<typename std::remove_reference_t<Ep>::Pointer>> 
    {
        using Type = typename std::remove_reference_t<Ep>::Pointer;
    };

public:
    using Pointer = typename Ptr_in<Tp, Dp>::Type;

    static_assert(!std::is_rvalue_reference<Dp>::value,
                  "unique_ptr's deleter type must be a function object type or an lvalue reference type");
    UniquePtrImpl_in() = default;
    UniquePtrImpl_in(Pointer p): ptr_impl_{} { ptr_impl_.first_ = p; }

    template<typename Del>
    UniquePtrImpl_in(Pointer p, Del&& d): ptr_impl_{p, spwuti::forward<Del>(d)} { }

    void Swap(UniquePtrImpl_in& other) noexcept
    {
        using spwstd::spwuti::swap;
        swap(ptr_impl_.first_, other.ptr_impl_.first_);
        swap(ptr_impl_.second_, other.ptr_impl_.second_);
    }

    Pointer& GetPtr() { return ptr_impl_.first_; }
    Pointer GetPtr() const { return ptr_impl_.first_; }
    Dp& GetDeleter() { return ptr_impl_.second_; }
    const Dp& GetDeleter() const { return ptr_impl_.second_; }

private:
    spwstd::spwuti::Pair<Pointer, Dp> ptr_impl_;
};

template<typename Tp, typename Dp = DefaultDelete<Tp>>
class UniquePtr {
    UniquePtrImpl_in<Tp, Dp> ptr_;

    // 函数对象
    template<typename Up>
    using DeleterConstraint = typename std::enable_if_t<
        std::__and_<
            std::__not_<std::is_pointer<Up>>,
            std::is_default_constructible<Up>>::value>;

public:
    using Pointer = typename UniquePtrImpl_in<Tp, Dp>::Pointer;
    using ElementType = Tp;
    using DeleterType = Dp;

private:
    template<typename Up, typename Ep>
    using SafeConversion_in = std::__and_<
        std::is_convertible<typename UniquePtr<Up, Ep>::Pointer, Pointer>,
        std::__not_<std::is_array<Up>>>;

public:
    // 构造函数, deleter为函数对象, 不能为函数指针
    template<typename Del=Dp, typename = DeleterConstraint<Del>>
    constexpr UniquePtr() noexcept
        : ptr_{} { }

    template<typename Del=Dp, typename = DeleterConstraint<Del>>
    explicit UniquePtr(Pointer p) noexcept
        : ptr_{p} { }

    UniquePtr(Pointer p,
              typename std::conditional<std::is_reference_v<DeleterType>, 
                  DeleterType, const DeleterType&>::type d) noexcept
        : ptr_{p, d} { }

    UniquePtr(Pointer p, 
              typename std::remove_reference<DeleterType>::type&& d) noexcept
        : ptr_{spwuti::move(p), spwuti::move(d)} 
    {
        static_assert(!std::is_reference_v<DeleterType>, "rvalue deleter bound to reference");
    }

    UniquePtr(UniquePtr&& other) noexcept
        : ptr_{other.Release(), spwuti::forward<DeleterType>(other.GetDeleter())}
    { }

    template<typename Up, typename Ep, typename = 
        spwdetail::Require<
            SafeConversion_in<Up, Ep>,
            typename std::conditional<std::is_reference<Dp>::value,
            std::is_same<Ep, Dp>,
            std::is_convertible<Ep, Dp>>::type >>
    UniquePtr(UniquePtr<Up, Ep>&& other) noexcept
        : ptr_{other.Release(), spwuti::forward<Ep>(other.GetDeleter())}
    { }

    // 禁止赋制
    UniquePtr(const UniquePtr&) = delete;
    UniquePtr& operator=(const UniquePtr&) = delete;

    ~UniquePtr() noexcept
    {
        auto& ptr = ptr_.GetPtr();
        if (ptr != nullptr)
            GetDeleter()(spwuti::move(ptr));
        ptr = Pointer{};
    }

    // operator
    UniquePtr& operator=(UniquePtr&& other) noexcept
    {
        Reset(other.Release());
        GetDeleter() = spwuti::forward<DeleterType>(other.GetDeleter());
        return *this;
    }

    UniquePtr& operator=(spwconfig::nullptr_t) noexcept
    {
        Reset();
        return *this;
    }

    template<typename Up, typename Ep>
    typename std::enable_if_t<
            SafeConversion_in<Up, Ep>::value,
        UniquePtr&>
    operator=(UniquePtr<Up, Ep>&& other) noexcept
    {
        Reset(other.Release());
        GetDeleter() = spwuti::forward<Ep>(other.GetDeleter());
        return *this;
    }


    // ElementType 可以是void
    typename std::add_lvalue_reference_t<ElementType>
    operator*() const {
        return *Get();
    }

    Pointer operator->() const noexcept
    {
        return Get();
    }

    explicit operator bool() const noexcept 
    { return Get() == Pointer{} ? false : true; }

    // 成员函数
    Pointer Get() const noexcept
    { return ptr_.GetPtr(); }

    DeleterType& GetDeleter() noexcept
    { return ptr_.GetDeleter(); }

    const DeleterType& GetDeleter() const noexcept
    { return ptr_.GetDeleter(); }

    Pointer Release() noexcept 
    {
        Pointer p = Get();
        ptr_.GetPtr() = Pointer{};
        return p;
    }

    void Reset(Pointer p = Pointer{}) noexcept
    {
        spwuti::swap(ptr_.GetPtr(), p);
        if (p != Pointer{})
            GetDeleter()(spwuti::move(p));
    }

    void Swap(UniquePtr& other) noexcept
    {
        ptr_.Swap(other.ptr_);
    }
};

/*数组对象的偏特化*/
// 数组特化版本，Pointer必须和ElementType*同
template<typename Tp, typename Dp>
class UniquePtr<Tp[], Dp>
{
public:
    using Pointer = typename UniquePtrImpl_in<Tp, Dp>::Pointer;
    using ElementType = Tp;
    using DeleterType = Dp;

private:
    // 保存指向pointer数组的指针, pointer必须Tp*
    UniquePtrImpl_in<Tp, Dp> ptr_;

    // 函数对象
    template<typename Up>
    using DeleterConstraint = typename std::enable_if_t<
        std::__and_<
            std::__not_<std::is_pointer<Up>>,
            std::is_default_constructible<Up>>::value>;

    template<typename Up, typename Ep>
    using SafeConversionUp_in = std::__and_<
        std::is_same<Pointer, ElementType*>,
        std::is_same<typename UniquePtr<Up, Ep>::Pointer, typename UniquePtr<Up, Ep>::ElementType*>,
        std::is_convertible<typename UniquePtr<Up,Ep>::ElementType(*)[], ElementType(*)[]>,
        std::is_array<Up>>;

public:
    template<typename Del = Dp, typename = DeleterConstraint<Del>>
    constexpr UniquePtr() noexcept
        : ptr_{} 
    { }

    template<typename Del = Dp, typename = DeleterConstraint<Del>>
    explicit UniquePtr(Pointer p) noexcept
        : ptr_{p} 
    { }

    UniquePtr(Pointer p, 
              typename std::conditional<std::is_reference_v<DeleterType>, 
                  DeleterType, const DeleterType&>::type d) noexcept
        : ptr_{p, d} { }

    UniquePtr(Pointer p, 
              typename std::remove_reference<DeleterType>::type&& d) noexcept
        : ptr_{spwuti::move(p), spwuti::move(d)} 
    {
        static_assert(!std::is_reference_v<DeleterType>, "rvalue deleter bound to reference");
    }

    UniquePtr(UniquePtr&& other) noexcept
        : ptr_{other.Release(), spwuti::forward<DeleterType>(other.GetDeleter())}
    { }

    template<typename Up, typename Ep, typename = 
        spwdetail::Require<
            SafeConversionUp_in<Up, Ep>,
            typename std::conditional<std::is_reference<Dp>::value,
            std::is_same<Ep, Dp>,
            std::is_convertible<Ep, Dp>>::type >>
    UniquePtr(UniquePtr<Up, Ep>&& other) noexcept
        : ptr_{other.Release(), spwuti::forward<Ep>(other.GetDeleter())}
    { }

    UniquePtr(const UniquePtr&) = delete;
    UniquePtr& operator=(const UniquePtr&) = delete;

    ~UniquePtr() noexcept
    {
        auto& ptr = ptr_.GetPtr();
        if (ptr != nullptr)
            GetDeleter()(ptr);
        ptr = Pointer{};
    }

    // operator
    UniquePtr& operator=(UniquePtr&& other) noexcept
    {
        Reset(other.Release());
        GetDeleter() = spwuti::forward<DeleterType>(other.GetDeleter());
        return *this;
    }

    UniquePtr& operator=(spwconfig::nullptr_t) noexcept
    {
        Reset();
        return *this;
    }

    template<typename Up, typename Ep>
    typename std::enable_if_t<
        std::__and_<
            SafeConversionUp_in<Up, Ep>,
            std::is_assignable<DeleterType&, Ep&&>
            >::value,
        UniquePtr&>
    operator=(UniquePtr<Up, Ep>&& other) noexcept
    {
        Reset(other.Release());
        GetDeleter() = spwuti::forward<Ep>(other.GetDeleter());
        return *this;
    }

    // ElementType 可以是void
    typename std::add_lvalue_reference_t<ElementType>
    operator[](spwconfig::size_t i) const {
        static_assert(*Get() != Pointer{});
        return Get()[i];
    }

    explicit operator bool() const noexcept 
    { return Get() == Pointer{} ? false : true; }

    // 成员函数
    Pointer Get() const noexcept
    { return ptr_.GetPtr(); }

    DeleterType& GetDeleter() noexcept
    { return ptr_.GetDeleter(); }

    const DeleterType& GetDeleter() const noexcept
    { return ptr_.GetDeleter(); }

    Pointer Release() noexcept 
    {
        Pointer p = Get();
        ptr_.GetPtr() = Pointer{};
        return p;
    }

    // p必须是Pointer一样的数组
    template<typename Up, typename = spwdetail::Require<std::is_same<Up, Pointer>>>
    void Reset(Pointer p) noexcept
    {
        spwuti::swap(ptr_.GetPtr(), p);
        if (p != Pointer{})
            GetDeleter()(p);
    }

    void Reset(spwconfig::nullptr_t = nullptr) noexcept
    {
        Reset(Pointer{});
    }

    void Swap(UniquePtr& other) noexcept
    {
        ptr_.Swap(other.ptr_);
    }
};

// 普通函数
// swap, ==, !=
template<typename T, typename D>
inline void
swap(UniquePtr<T,D>& x, UniquePtr<T,D>& y) noexcept {
    x.Swap(y);
}

// UniquePtr的==比较只比较所指对象，不比较deleter函数对象
// 即使两个UniquePtr的deleter函数对象天差地别，只要所指对象相同，也相同
template<typename T, typename D,
         typename U, typename E>
inline bool
operator==(const UniquePtr<T, D>& x,
           const UniquePtr<U, E>& y) {
    return x.Get() == y.Get();
}

template<typename T, typename D>
inline bool
operator==(const UniquePtr<T, D>& x, spwconfig::nullptr_t) {
    return !x;
}

template<typename T, typename D>
inline bool
operator==(spwconfig::nullptr_t, const UniquePtr<T, D>& x) {
    return !x;
}

template<typename T, typename D,
         typename U, typename E>
inline bool
operator!=(const UniquePtr<T, D>& x,
           const UniquePtr<U, E>& y) {
    return x.Get() != y.Get();
}

template<typename T, typename D>
inline bool
operator!=(const UniquePtr<T, D>& x, spwconfig::nullptr_t) {
    return static_cast<bool>(x);
}

template<typename T, typename D>
inline bool
operator!=(spwconfig::nullptr_t, const UniquePtr<T, D>& x) {
    return static_cast<bool>(x);
}

// make_unique
// 不是数组
template<class T, class... Args>
typename std::enable_if<!std::is_array<T>::value, UniquePtr<T>>::type 
makeUnique(Args&&... args) {
    return UniquePtr<T>(new T(spwuti::forward<Args>(args)...));
}

template<class>
constexpr bool IsUnboundedArray_v = false;

template<class T>
constexpr bool IsUnboundedArray_v<T[]> = true;

template<class>
constexpr bool IsBoundedArray_v = false;

template<class T, spwconfig::size_t N>
constexpr bool IsBoundedArray_v<T[N]> = true;
// 数组不允许明确边界
template<class T>
typename std::enable_if<IsUnboundedArray_v<T>, UniquePtr<T>>::type
makeUnique(spwconfig::size_t n){
    return UniquePtr<T>(new typename std::remove_extent<T>::type[n]);
}

template<class T, class... Args>
typename std::enable_if<IsBoundedArray_v<T>>::type 
makeUnique(Args&&...) = delete;
} // namespace spwptr
} //namespace spwstd
#endif // SPW_UNI_PTR_H
